<?php

/** 
 * Working with roles, rules and resources
 * @category Nimblecms Core
 * @package Nimblecms_Acl
 * @author kenny
 * @copyright Copyright (c) 2011
 * 
 */
class Nimblecms_Acl extends Zend_Acl {
	
	static private $_instance = null;
	
	protected $_roleModel;
	protected $_roleParentModel;
	protected $_resourceModel;
	protected $_ruleModel;
	protected $_rulePrivileges;
	protected $_privileges;
	
	/**
	* Return Acl object
	* @return object Nimblecms_Acl
	*/
	static public function getInstance() {
		if(self::$_instance === null) {
			self::$_instance = new Nimblecms_Acl();
		}
		return self::$_instance;
	}
	
	/**
	* Get ACL
	* @return Nimblecms_Acl
	*/
	public function getAcl() {
		$this->_roleModel = new Default_Model_Roles();
		$this->_roleParentModel = new Default_Model_RolesParents();
		$this->_resourceModel = new Default_Model_Resources();
		$this->_ruleModel = new Default_Model_Rules();
		$this->_rulePrivileges = new Default_Model_RulesPrivileges();
		$this->_privileges = new Default_Model_Privileges();
		
		/**
		* Loading roles, rules, resources
		*/
		$this->_loadRoles();
		$this->_loadResources();
		$this->_loadRules();
		
		return $this;
	}
	
	public function __construct() {}
	
	/**
	* Load roles
	*/
	private function _loadRoles() {
		$roles = $this->_roleModel->fetchAll();
		foreach ($roles as $role) {
		    $this->_loadRole($role);
		}
	}
	
	private function _loadRole($role, $inheritanceChain = array())
	{	
		if (in_array($role->name, $inheritanceChain)) {
			$inheritanceChain[] = $role->name;
			throw new Exception('Роль уже присутствует в цепочке наследования : ' . implode('->', $inheritanceChain));
		}
		$parents = $role->findManyToManyRowset($this->_roleModel, $this->_roleParentModel, 'Role', 'Parent');
		if ($parents->count() > 0) {
			foreach ($parents as $parent) {
				$inheritanceChain[] = $role->name;
				$this->_loadRole($parent, $inheritanceChain);
			}
		}
		if (!$this->hasRole(new Zend_Acl_Role($role->name))) {
		   if ($parents->count() > 0){   	 
		      $this->addRole(new Zend_Acl_Role($role->name), $this->_prepareArray($parents->toArray())); 
		   }else {
		      $this->addRole(new Zend_Acl_Role($role->name)); 
		   }
		}
	}
	
	/**
	* Load Resources
	*/
	private function _loadResources() {
		$resources = $this->_resourceModel->fetchAll();
		foreach ($resources as $resource) {
			$this->_loadResource($resource);
		}
	}
	
	private function _loadResource($resource)
	{
		$parent = $resource->findParentRow($this->_resourceModel, 'Parent');  
		if ($parent) {		
				$this->_loadResource($parent);
		}
		if (!$this->has(new Zend_Acl_Resource($resource->name))) {
		   if ($parent) {
		      $this->add(new Zend_Acl_Resource($resource->name), $parent->name); 
		   }else {
		      $this->add(new Zend_Acl_Resource($resource->name)); 
		   }
		}
	}
	
	/**
	* Load Rules
	*/
	private function _loadRules() {
		$rules = $this->_ruleModel->fetchAll();
		foreach ($rules as $rule) {				
			$assert = $rule->assert;
			if ($assert) {
			    $assert = new $assert();
			} else {
				$assert = null;
			}
			$privileges = $this->_prepareArray($rule->findManyToManyRowset($this->_privileges, $this->_rulePrivileges, 'Rule', 'Privilege')->toArray());
			$privileges = count($privileges) > 0 ? $privileges : null;
			switch ($rule->type) {
				case 'allow':
					$this->allow(new Zend_Acl_Role($rule->findParentRow($this->_roleModel)->name), new Zend_Acl_Resource($rule->findParentRow($this->_resourceModel, 'Resource')->name), $privileges, $assert);
					break;
				case 'deny':
					$this->deny(new Zend_Acl_Role($rule->findParentRow($this->_roleModel)->name), new Zend_Acl_Resource($rule->findParentRow($this->_resourceModel, 'Resource')->name), $privileges, $assert);
					break;
			}
		}
	}
	
	/**
     * Prepare arrays
     * @param Array
     * @return Array
     */
    private function _prepareArray($array)
    {
    	$count = count($array);
    	$temp = array();
    	for ($i = 0; $i < $count; $i++) {
    		$temp[] = $array[$i]['name'];
    	}
    	return $temp;
    }
}